#include <cstdlib>
#include <iostream>
#include <typeinfo>
#include <cxxabi.h>
#include "UnitTest/TestRunner.hpp"

namespace unit_test {

TestRunner::TestRunner(const std::string &suiteName) {
  mPassedCount = 0;
  mFailedCount = 0;
  mSuiteName = suiteName;
}

TestRunner::TestRunner(const TestRunner &from) {
  mPassedCount = from.mPassedCount;
  mFailedCount = from.mFailedCount;
  mSuiteName = from.mSuiteName;
  mCaseFunctionsList = from.mCaseFunctionsList;
}

TestRunner::~TestRunner() {

}

TestRunner &TestRunner::operator=(const TestRunner &from) {
  if (this != &from) {
    mPassedCount = from.mPassedCount;
    mFailedCount = from.mFailedCount;
    mSuiteName = from.mSuiteName;
    mCaseFunctionsList = from.mCaseFunctionsList;
  }
  return *this;
}

void TestRunner::Run(bool verbose) {
  std::list<TestCasesData>::const_iterator it;
  unsigned int testCasesCount = 0;

  std::cout << "\033[1;34mRunning suite: " << mSuiteName << "\033[0m"
      << std::endl;
  for (it = mCaseFunctionsList.begin(); it != mCaseFunctionsList.end(); ++it) {
    if (verbose)
      std::cout << it->file << ":Func " << it->name << ": ";
    StartTestCase(*it);
    ++testCasesCount;
  }
  float percentage = (static_cast<float>(mPassedCount)
      / static_cast<float>(mCaseFunctionsList.size())) * 100;
  std::cout << static_cast<int>(percentage) << "%: Passed: \033[1;32m"
      << mPassedCount
      << "\033[0m Failed: \033[1;31m" << mFailedCount << "\033[0m" << std::endl;
}

void TestRunner::StartTestCase(const TestCasesData &data) {
  char *realName;
  int status;

  try {
    std::cout.flush();
    (data.function)();
    ++mPassedCount;
    std::cout << "Passed" << std::endl;
  }
  catch (TestFailureException &e) {
    mFailedCount++;
    std::cout << "\033[1;31mFailed\033[0m (" << e.what() << ")" << std::endl;
  }
  catch (std::exception &e) {
    realName = abi::__cxa_demangle(typeid(e).name(), 0, 0, &status);
    ++mFailedCount;
    std::cout << "Failed (caught an exception: [" << realName
	<< " ; \"" << e.what() << "\"])" << std::endl;
    free(realName);
  }
  catch (...) {
    ++mFailedCount;
    std::cout << "Failed (caught an exception of unknown type)" << std::endl;
  }
}

void TestRunner::AddTestCase(const std::string &file, unsigned int line,
    const std::string &name, CaseFunction *function) {
  TestCasesData newTestCaseData = { function, name, file, line };
  size_t pos = newTestCaseData.file.find_last_of("/");
  if (pos != std::string::npos)
    newTestCaseData.file.assign(file.begin() + pos + 1, file.end());
  mCaseFunctionsList.push_back(newTestCaseData);
}

} /* !namespace unit_test */
